#include "Vector2D.h"



CFigure2D::CFigure2D()
{
	m_lines = new lines();
  m_lineiterator = new lineiterator();
  //m_ximage = new CxImage();
  m_serialize = false;
}

CFigure2D::~CFigure2D()
{
  //for((*m_lineiterator) = m_lines->begin(); 
  //      (*m_lineiterator) != m_lines->end();
  //      (*m_lineiterator)++)
  //{
    //delete (m_pointiterator);
	  //delete (*(*m_lineiterator));
  //}
  CleanPoints();
  //delete(m_ximage);
  delete(m_lines);
  delete(m_lineiterator);
}

int CFigure2D::AddPoint(POINT & point)
{
  //int i;
  //m_npoints ++;
	m_points->push_back(point);

  if(m_points->size() > 1 && !m_serialize)
    CheckCloseLine(point);
  //i = m_points->
  return (m_points->size()); //(
}

void CFigure2D::UpdatePoint(POINT & pt,int index)
{
  *m_pointiterator[index] = pt; 
}


POINT & CFigure2D::GetFirstPoint()
{
  m_npoints = 0;
  m_ptIter = m_points->begin();
  return(*(m_ptIter));
}

POINT & CFigure2D::GetNextPoint()
{
  //POINT pt;
  m_npoints ++;
  if(m_npoints == m_points->size())
  {
    m_npoints = 0;
    m_ptIter = m_points->begin(); //we set the iterator to begin list
    return(*(m_ptIter));
  }
  //pt = *((*m_pointiterator)++);
  return(*(++m_ptIter));
}

POINT & CFigure2D::GetPrevPoint()
{
  m_npoints --;
  if(m_npoints == -1)
  {
    m_npoints = GetSize() - 1;
    m_ptIter = m_points->end(); //we set the iterator to begin list
    //return(*(*m_pointiterator));
  }
  //pt = *((*m_pointiterator)++);
  return(*(--m_ptIter));
}

int CFigure2D::GetSize()
{
  return (m_points->size());
}

POINT & CFigure2D::GetLastPoint()
{
  m_npoints = GetSize() - 1;
  m_ptIter = m_points->end();
	return(*(--m_ptIter));
}

//direction = true -> save
bool CFigure2D::Serialize(bool direction)
{
  return true;
}

bool CFigure2D::IsClosedLine()
{
  //This method will check if the last point is equal to first one
  if(m_points->back().x == m_points->front().x &&
     m_points->back().y == m_points->front().y)
    return true;
  else
    return false;
}

void CFigure2D::CheckCloseLine(POINT & point)
{
  //Check if the point is close to first point of line (POINT_LIMIT)
  POINT pt;
  int   size = 0;
  int   error = 0;

  pt = *(m_points->begin()); //it always points to current line 
  
  if( (point.x - POINT_LIMIT) < pt.x && pt.x < (point.x + POINT_LIMIT) &&
      (point.y - POINT_LIMIT) < pt.y && pt.y < (point.y + POINT_LIMIT))
  {
    //we have to close the line (if the line should be close
    if(MessageBox(NULL,"Close border?","Question",MB_YESNO | MB_ICONQUESTION | MB_TASKMODAL | MB_DEFBUTTON1) == IDYES)
    {
      
      //*(m_points->end()) = *(m_points->begin());
      //*m_pointiterator[m_points->size() - 1] = *(m_points->begin());
      //this->UpdatePoint(*(m_points->begin()),m_points->size() - 1);
      m_points->pop_back();
      m_points->push_back(*(m_points->begin()));
      //We have to call EndLine
      //The best way is to SendMessage to main window !!!!

      SendMessage(m_hwnd,WM_COMMAND,(WPARAM) IDM_DRAW_BORDER,0); //Maybe we should mark this message it commes from child??
    }
    error = GetLastError();
  }
}

void CFigure2D::DrawFigure(const HWND & hwnd, bool _bCoordinates)
{
  HDC hdc = GetDC(hwnd);
  m_hwnd = hwnd;
  DrawFigure(hwnd, hdc, _bCoordinates);
}


  
void CFigure2D::DrawFigure(const HWND & hwnd, HDC & hdc, bool _bCoordinates)
{
  long i=0;
  POINT pt;
  
  points p;
  pointiterator pi;
  char * coord;
  m_hwnd = hwnd;
  coord = new char [40];

  HPEN   newpen,oldpen;
  HBRUSH newbrush,oldbrush;

  newpen = CreatePen(PS_SOLID,2,RGB(255,25,0));
  newbrush = CreateSolidBrush(RGB(255,25,0));

  oldpen    = (HPEN) SelectObject(hdc,newpen);
  oldbrush  = (HBRUSH) SelectObject(hdc,newbrush);
  //The lines loop
  for((*m_lineiterator) = m_lines->begin(); 
        (*m_lineiterator) != m_lines->end();
        (*m_lineiterator)++)
  {
    //p = (*(*m_lineiterator)).begin();
    //pi = p::iterator;
    if(!(*(*m_lineiterator))->empty() )
    {
      pt = *((*(*m_lineiterator))->begin());
      MoveToEx(hdc,pt.x,pt.y,NULL);
    }
    for(pi = (*(*m_lineiterator))->begin(); 
          pi != (*(*m_lineiterator))->end();
          pi++)
    {
        pt = *pi;
        
        //oldpen    = (HPEN) SelectObject(hdc,newpen);
        //oldbrush  = (HBRUSH) SelectObject(hdc,newbrush);

        LineTo(hdc,pt.x,pt.y); //The pointer is not initialized so I should use the local property
    }
  }

  if(_bCoordinates)
  {

    for((*m_lineiterator) = m_lines->begin(); 
          (*m_lineiterator) != m_lines->end();
          (*m_lineiterator)++)
    {
      for(pi = (*(*m_lineiterator))->begin(); 
            pi != (*(*m_lineiterator))->end();
            pi++)
      {
          pt = *pi;
          SelectObject(hdc,oldpen);
          SelectObject(hdc,oldbrush);
          
          memset(coord,0,30);
          _itoa_s(pt.x,coord,5,10);
          strcpy_s(&coord[strlen(coord)],2,",");
          _itoa_s(pt.y,&coord[strlen(coord)],5,10);
          //strcpy_s(&coord[5],2,",");
          //DrawText(hdc,coord,10,
          TextOut(hdc,pt.x,pt.y,coord,strlen(coord));
      }
    }
  }
  //MoveToEx(hdc,508,418,NULL);
  //LineTo(hdc,500,428);
  /*
  if(!m_points->empty())
  {
    pt = *m_points->begin();
    MoveToEx(hdc,pt.x,pt.y,NULL);
  }
  for((*m_pointiterator) = m_points->begin(); 
        (*m_pointiterator) != m_points->end();
        (*m_pointiterator)++)
  {
      pt = *(*m_pointiterator);
      LineTo(hdc,pt.x,pt.y);
  }
*/

  //SelectObject(hdc,oldpen);
  DeleteObject(newpen);
  //SelectObject(hdc,oldbrush);
  DeleteObject(newbrush);
  ReleaseDC(m_hwnd, hdc);

  delete(coord);
}



void CFigure2D::BeginLine()
{
  m_npoints = 0;
  m_points = new points();
  m_pointiterator = new pointiterator;
  m_lines->push_back(m_points);
}
void CFigure2D::EndLine()
{
  
  //delete(m_points);
  delete(m_pointiterator);
  m_pointiterator = NULL;
}


void CFigure2D::RemoveCurrentFigure()
{
  if(m_lines->size() > 0)
  {
    m_lines->pop_back();
    if(m_pointiterator)
    {
      delete(m_pointiterator);
      m_pointiterator = NULL;
    }
    m_points->clear();
    if(m_lines->size() > 0)
      m_points = m_lines->back();
  }
}


void CFigure2D::CleanPoints()
{

  for((*m_lineiterator) = m_lines->begin(); 
        (*m_lineiterator) != m_lines->end();
        (*m_lineiterator)++)
  {

      delete(*(*m_lineiterator));
  }
  
  m_lines->clear();
  
  //delete(m_pointiterator)
}



void CFigure2D::InitPen(COLORREF & col)
{
  HPEN    newpen;
  HBRUSH  newbrush;

  newpen = CreatePen(PS_SOLID,4,RGB(255,0,0));
  newbrush = CreateSolidBrush(RGB(255,0,0));

}



void CFigure2D::ShowPicture(HWND & hwnd)
{
  /*
  //CComPtr<IFileOpenDialog> pDlg;
  //IFileDialog *pfd;
  _TCHAR * filename = new _TCHAR [4000];
  // CoCreate the dialog object.
  //HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, 
  //                              NULL, 
  //                              CLSCTX_INPROC_SERVER, 
  //                              IID_PPV_ARG(IFileDialog, &pfd));
  //HDC hdc_tmp = CreateDC("DISPLAY",NULL,NULL,NULL);
  HDC hdc_tmp = CreateCompatibleDC(GetDC(hwnd));
  OPENFILENAME opf;
  ZeroMemory(&opf, sizeof(opf));
  ZeroMemory(filename, 4000);
  opf.hwndOwner = hwnd;
  opf.lpstrFilter = _T("All Files\0*.*\0\0");
  opf.lpstrCustomFilter = NULL;
  opf.nFilterIndex = 1;
  opf.lpstrFile = filename;
  opf.nMaxFile = 4000;
  opf.lpstrFileTitle = NULL;
  opf.lpstrInitialDir = NULL;
  opf.lpstrTitle = NULL;
  opf.nMaxFileTitle = 0;
  opf.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
  //opf.Flags = OFN_DONTADDTORECENT | OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_LONGNAMES;
  opf.lStructSize = sizeof(opf);

  if(GetOpenFileName(&opf))
  {


    //This method create/draw picture in memmory HDC and afterthat make very fast (alligned to window size)
    //bitblt to window HDC, it means we can very fast process the picture/photo
    //and additionaly we can provide all processing on memory hdc what can be fastest :)
    HBITMAP bm_back,bm_oryg;
    HDC     hdc_back,hdc_oryg;
    RECT    rct;


    m_ximage->Load(opf.lpstrFile);
    //Prepare copy of HDC and create copy of bitmap
    GetClientRect(hwnd,&rct);
    hdc_oryg = GetDC(hwnd);
    hdc_back = CreateCompatibleDC(hdc_oryg);
    bm_back = CreateCompatibleBitmap(hdc_oryg,rct.right, rct.bottom);
    bm_oryg = (HBITMAP) SelectObject(hdc_back,bm_back);
    
    m_ximage->Draw(hdc_back,0,0); //,m_ximage->GetWidth(),m_ximage->GetHeight());
    //Drawing by copy buffer
    //BitBlt(hdc_oryg,0,0,rct.right,rct.bottom,hdc_back,0,0,SRCCOPY);
    StretchBlt(hdc_oryg,0,0,rct.right,rct.bottom,hdc_back,0,0,m_ximage->GetWidth(),m_ximage->GetHeight(),SRCINVERT);
    //Ending
    SelectObject(hdc_back,bm_oryg);
    DeleteDC(hdc_back);
    DeleteObject(bm_back);
    ReleaseDC(hwnd,hdc_oryg);


    //Step by step how to draw the map and figures
    //1. Create a base HDC for a main map
    //2. The size of HDC (in memory) has to have a size of original picture
    //3.Use the stretchblt to copu memory HDC into screen HDC
    //4. Analyse the memory HDC to see where the line/border exists
    //5. Create a second HDC in memory to keep the line
    //6. During Redraw use extra memory HDC where all memory HDC will be copy (bitblt) with option SRCAND
    //7. Redraw will copy/stretchblt to screen HDC !!


  }
  //int error = CommDlgExtendedError();
  */

}


bool CFigure2D::IsPointInside(const POINT & point)
{
  //We have to find a 4 point which are closest to given point.
  //There is other algorith which check if the point is inside the figure
  //if the line cross even time the point is inside
  //So we will have to create a vector from all point of figure
  //We can optimize the aloritm and take only point from figure which are 
  //allign to pint
  POINT pt={0,0};
  pointiterator pi;
  int       cross = 0;

  Vector2D c0(pt,(POINT)point);

  for((*m_lineiterator) = m_lines->begin(); 
        (*m_lineiterator) != m_lines->end();
        (*m_lineiterator)++)
  {
    pi = (*(*m_lineiterator))->begin();
    pt = *pi;
    for(pi++; 
          pi != (*(*m_lineiterator))->end();
          pi++)
    
    {
      Vector2D c1(pt,*pi);
      if(c1.CheckCollision(c0))
        cross++;
      pt = *pi;
    }
  }
  if( cross & 0x01)  //bitwise and check the last bit for parity odds or even :) 
    return true;
  else
    return false;
}

bool CFigure2D::GetCirculation()
{
  //We have to find a smallest x-coordinate point 
  //from this point we have to get previous point and next
  //and check if the next point has a smaller coordinates then previous
  //TODO: it returns only current figure, and forgot about previous
  int i = 0;
  pointiterator it;
  POINT p={2000,0},prev,next;

  for(it = ++ m_points->begin() ;
      it != -- m_points->end();
      it ++)
  {
    i++;
    if(p.x > (*it).x)
    {
      p = *it;
      if(i == 1)
        prev = m_points->back();
      else
      {
        it--;
        prev = *it;
        it++;
      }
      if(i == m_points->size())
        next = m_points->front();
      else
      {
        it++;
        next = *it;
        it--;
      }
    }
  }
  
  Vector2D v1(prev,p),v2(p,next);
  double product;

  product = v1.GetProduct(v2);
  if(product < 0 )
  {
    //We have wrong circulation
    return false;
  }

  return true;
}

/*
void DrawOnCopy(HWND & hwnd)
{
  HBITMAP bm_back,bm_oryg;
  HDC     hdc_back,hdc_oryg;
  RECT    rct;


  m_ximage->Load(opf.lpstrFile);
  //Prepare copy of HDC and create copy of bitmap
  GetClientRect(hwnd,&rct);
  hdc_oryg = GetDC(hwnd);
  hdc_back = CreateCompatibleDC(hdc_oryg);
  bm_back = CreateCompatibleBitmap(hdc_oryg,&rct.right, &rct.bottom);
  bm_oryg = (HBITMAP) SelectObject(hdc_back,bm_back);
  
  m_ximage->Draw(hdc_back,0,0,rct.right,rct.bottom);
  //Drawing by copy buffer
  BitBlt(hdc_oryg,0,0,rct.right,rct.bottom,hdc_back,0,0,SRCCOPY);

  //Ending
  SelectObject(hdc_back,bm_oryg);
  DeleteDC(hdc_back);
  DeleteObject(bm_back);
  ReleaseDC(hwnd,hdc_oryg);
  //UpdateWindow(hwnd);
}*/


bool CFigure2D::IsEdgeIntersect()
{

  //We should check the intersection 


  bool ret = false;

  pointiterator it, int_it;
  POINT pt, vt, int_pt, int_vt;
  Vector2D v1,v2;
  
  pt = *(m_points->begin());
  for(it = ++ m_points->begin() ;
      it != --m_points->end();
      it ++)
  {
    vt = *it;
    v1.SetVector(pt, vt);
    
    int_pt = vt;
    
    int_it = it;
    int_it++;
    for(;
        int_it != m_points->end();
        int_it ++)
    {
      int_vt = *int_it;
      v2.SetVector(int_pt, int_vt);
      int_pt = int_vt;

      if(v1.CheckCollision(v2,false))
        return true;
    }
    pt = vt;
  }

  return ret;
}

void CFigure2D::EliminateSubPoints()
{
  //In case the point lays on the same line like points prev and next 
  //means the vectors are equal we will remove this point from the list 
  
  pointiterator it,jt, kt;
  POINT first, second, third;
  Vector2D v1, v2;

  v1.SetLengthInsensitive(true);

  for(it = m_points->begin();
      it != --(--(--(m_points->end())));
      it ++)
  {
    //In case we remove current element pointed by 
    //iterator we have to refresh iterator
    //by moving backward :)
    //but we have to check also begining and end of the figure
    kt = it;
    for(jt = ++kt;
        jt != --(--(m_points->end()));
        jt++)
    {
      //In case it->jt and jt->jt++ are different vectors just break; :)
      first  = *it;
      second = *jt;
      third  = *(++jt);
      jt--;
      v1.SetVector(first, second);
      v2.SetVector(second, third);
      if(v1 != v2)
        break;
      else
      {
        jt = m_points->erase(jt);
        jt --;
      }
    }    
  }
}